"""
BeaverFramework Model Module

This module provides the base Model class for creating simulation components in the BeaverFramework.
It defines the fundamental structure for all models in the framework, enabling data flow and
interconnections between different simulation components.

Authors:
    MichaelZhang

Key Features:
    - Abstract base class for all simulation models
    - Input/output management system
    - Model linking capabilities
    - Standardized computation interface

Usage:
    class MyModel(Model):
        def __init__(self):
            super().__init__("my_model")
            
        def setup(self):
            # Initialize model parameters
            pass
            
        def compute(self):
            # Perform calculations
            pass
"""

import os
from os.path import join
from abc import ABCMeta
from json import load
from collections import OrderedDict

class Model(metaclass=ABCMeta):
    """
    Abstract base class for all simulation models in the framework.
    
    This class provides the foundation for creating modular simulation components
    that can be connected together to form complex simulation networks. Each model
    manages its own inputs and outputs, and can be linked to other models to create
    data flow paths.

    Attributes:
        name (str): Unique identifier for the model
        inputs (dict): Dictionary storing input values
        outputs (dict): Dictionary storing output values
        input_links (dict): Maps input names to (source_model, output_name) tuples
        output_links (dict): Maps output names to list of (target_model, input_name) tuples
    """

    def __init__(self, name):
        """
        Initialize a new Model instance.

        Args:
            name (str): Unique identifier for the model
        """
        self.name = name
        self.inputs = {}
        self.outputs = {}
        self.input_links = {}
        self.output_links = {}

    def setup(self):
        """
        Set up the model before simulation.
        
        This method should be overridden by subclasses to initialize any necessary
        parameters or states before the simulation begins.
        """
        pass

    def compute(self):
        """
        Perform the model's computations.
        
        This method should be overridden by subclasses to implement the actual
        computational logic of the model. It should use the values in self.inputs
        pass

    def link_input(self, input_name, source_model, output_name):
        self.input_links[input_name] = (source_model, output_name)

    def link_output(self, output_name, target_model, input_name):
        if output_name not in self.output_links:
            self.output_links[output_name] = []
        self.output_links[output_name].append((target_model, input_name))

    def update_inputs(self):
        for input_name, (source_model, output_name) in self.input_links.items():
            self.inputs[input_name] = source_model.outputs[output_name]

    def update_outputs(self):
        for output_name, links in self.output_links.items():
            for target_model, input_name in links:
                target_model.inputs[input_name] = self.outputs[output_name]
